// Copyright (c) 2020 Intel Corporation.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

/**
 * @brief Message bus echo service example
 */

#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <server.h>
#include "eii/msgbus/msgbus.h"
#include "eii/utils/logger.h"
#include "eii/utils/json_config.h"

using namespace eii::config_manager;

Server::Server(std::atomic<bool> *loop) {
    server_ch = new ConfigMgr();
    this->loop = loop;
}

Server::~Server() {
    if (msg != NULL)
        msgbus_msg_envelope_destroy(msg);
    if (parts != NULL)
        msgbus_msg_envelope_serialize_destroy(parts, num_parts);
    if (g_service_ctx != NULL)
        msgbus_recv_ctx_destroy(g_msgbus_ctx_server, g_service_ctx);
    if (g_msgbus_ctx_server != NULL)
        msgbus_destroy(g_msgbus_ctx_server);
}

bool Server::init() {
    server_ch = new ConfigMgr();

    int num_of_servers = server_ch->getNumServers();
    if (num_of_servers == -1) {
        LOG_ERROR_0("No server instances found, exiting...");
        return false;
    }

    // ServerCfg* server_ctx = server_ch->getServerByName("echo_service");
    ServerCfg* server_ctx = server_ch->getServerByIndex(0);
    config_t* service_config = server_ctx->getMsgBusConfig();

    g_msgbus_ctx_server = msgbus_initialize(service_config);
    if (g_msgbus_ctx_server == NULL) {
        LOG_ERROR_0("Failed to initialize message bus");
        goto err;
    }

    msgbus_ret_t ret;
    ret = msgbus_service_new(g_msgbus_ctx_server, "echo_service", NULL,
                             &g_service_ctx);
    if (ret != MSG_SUCCESS) {
        LOG_ERROR("Failed to initialize service (errno: %d)", ret);
        goto err;
    }

    return true;
err:
    if (g_service_ctx != NULL)
        msgbus_recv_ctx_destroy(g_msgbus_ctx_server, g_service_ctx);
    if (g_msgbus_ctx_server != NULL)
        msgbus_destroy(g_msgbus_ctx_server);

    return false;
}

void* Server::start(void *arg) {
    int ret_val = -1;
    Server *obj = reinterpret_cast<Server*>(arg);

    if ((ret_val = obj->server_service()) != 0) {
        LOG_ERROR("server failed.(errno: %d)", ret_val);
    }
    delete obj;
}

int Server::server_service() {
    int ret = 0;
    LOG_INFO_0("Running...");
    while (*loop) {
                ret = msgbus_recv_wait(g_msgbus_ctx_server, g_service_ctx,
                                       &msg);
        if (ret != MSG_SUCCESS) {
            // Interrupt is an acceptable error
            if (ret == MSG_ERR_EINTR)
                break;
            LOG_ERROR("Failed to receive message (errno: %d)", ret);
            goto err;
        }

        num_parts = msgbus_msg_envelope_serialize(msg, &parts);
        if (num_parts <= 0) {
            LOG_ERROR_0("Failed to serialize message");
            goto err;
        }
        LOG_INFO("Received Request at server: %s", parts[0].bytes);
        // Responding
        ret = msgbus_response(g_msgbus_ctx_server, g_service_ctx, msg);
        if (ret != MSG_SUCCESS) {
            LOG_ERROR("Failed to send response (errno: %d)", ret);
            goto err;
        }

        msgbus_msg_envelope_serialize_destroy(parts, num_parts);
        msgbus_msg_envelope_destroy(msg);
        msg = NULL;
        parts = NULL;
    }

    if (parts != NULL)
        msgbus_msg_envelope_serialize_destroy(parts, num_parts);

    return 0;

err:
    if (msg != NULL)
        msgbus_msg_envelope_destroy(msg);
    if (parts != NULL)
        msgbus_msg_envelope_serialize_destroy(parts, num_parts);
    if (g_service_ctx != NULL)
        msgbus_recv_ctx_destroy(g_msgbus_ctx_server, g_service_ctx);
    if (g_msgbus_ctx_server != NULL)
        msgbus_destroy(g_msgbus_ctx_server);
    return -1;
}
